package com.bobo.mm.socket.provider;

import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;

import com.bobo.mm.ClientConfig;
import com.bobo.mm.socket.InnerSocketListener;
import com.bobo.mm.socket.decoder.IDecoder;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;

/**
 * Created by cocolove2 on 2020/8/21.
 */
public class NioSocketClient extends BaseSocketClient {
    private static final String TAG = "NioSocketClient";
    private final InetSocketAddress mSocketAddress;
    private SocketChannel mSocketChannel;
    private Selector mSelector;
    /**
     * 主socket线程
     */
    private HandlerThread mSocketThread;
    private Handler mSocketHandler;
    /**
     * 读缓存区
     */
    private final ByteBuf mReadByteBuffer;
    /**
     * 写缓存区
     */
    private final ByteBuf mWriteByteBuffer;

    private final String clientId;
    private final IDecoder mDecoder;
    /**
     * 完整数据帧缓存
     */
    private final List<byte[]> mFrameList = new ArrayList<>();

    public NioSocketClient(ClientConfig clientConfig, InnerSocketListener listener, IDecoder decoder) {
        super(clientConfig, listener);
        if (decoder == null) {
            throw new IllegalArgumentException("AFrameHandler不能为空");
        }
        mDecoder = decoder;
        this.clientId = clientConfig.getClientId();
        mReadByteBuffer = Unpooled.buffer(8092);
        mWriteByteBuffer = Unpooled.buffer(8092);
        String[] arr = getClientConfig().getSocketUrl().split(":");
        mSocketAddress = new InetSocketAddress(arr[0], Integer.parseInt(arr[1]));
    }


    @Override
    public void startConnect() {
        if (mSocketThread == null) {
            mSocketThread = new HandlerThread("socket-thread");
            mSocketThread.start();
            mSocketHandler = new Handler(mSocketThread.getLooper());
        }
        mSocketHandler.post(new Runnable() {
            @Override
            public void run() {
                init();
            }
        });
    }

    private void init() {
        try {
            // 创建一个客户端SocketChannel对象
            mSocketChannel = SocketChannel.open();
            // 设置客户端Channel为非阻塞模式
            mSocketChannel.configureBlocking(false);
            // 创建一个供给客户端使用的Selector对象
            mSelector = Selector.open();
            // 注册客户端Channel到Selector中，这里客户端Channel首先监听的是OP_CONNECT事件，
            // 因为其首先必须与服务器建立连接，然后才能发送和读取数据
            mSocketChannel.register(mSelector, SelectionKey.OP_CONNECT);
            setClientConnState(CONNECTING);
            stopReconHandler();
            // 调用客户端Channel.connect()方法连接服务器，需要注意的是，该方法的调用必须放在
            // 上述Channel.register()方法之后，否则在注册之前客户端就已经注册完成，
            // 此时Selector就无法收到SelectionKey.OP_CONNECT事件了
            mSocketChannel.connect(mSocketAddress);
            while (!mSocketThread.isInterrupted()) {
                // 监听客户端Channel的事件，这里会一直等待，直到有监听的事件到达。
                // 对于客户端，首先监听到的应该是SelectionKey.OP_CONNECT事件，
                // 然后在后续代码中才会将SelectionKey.OP_READ和WRITE事件注册
                // 到Selector中
                mSelector.select(1000);
                if (!mSelector.isOpen() || getClientConnState() == DISCONNECTED) {
                    break;
                }
                //先处理写数据
                doWrite();
                Set<SelectionKey> selectionKeys = mSelector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()) {
                    SelectionKey key = iterator.next();
                    iterator.remove();
                    // 监听到客户端Channel的SelectionKey.OP_CONNECT事件，并且处理该事件
                    if (key.isConnectable()) {
                        doConnecting(key);
                    } else if (key.isReadable()) {
                        // 监听到客户端Channel的SelectionKey.OP_READ事件，并且处理该事件
                        read(key);
                    }
                }
            }

        } catch (IOException e) {
            e.printStackTrace();
            failError(e);
        }
    }

    @Override
    public void stopConnect() {
        try {
            if (mSocketChannel != null) {
                mSocketChannel.close();
            }
            if (mSelector != null) {
                mSelector.close();
            }
            getSocketListener().onClosed(clientId, 0, "ok");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            super.stopConnect();
            mWriteByteBuffer.clear();
            mReadByteBuffer.clear();
            if (mSocketThread != null) {
                mSocketThread.quitSafely();
                mSocketHandler = null;
                mSocketThread = null;
            }
        }
    }

    @Override
    void reconnect(String clientId, int reconCount) {
        setClientConnState(RECONNECT);
        getSocketListener().onReconnecting(getClientConfig().getClientId(), reconCount);
        startConnect();
    }


    @Override
    public boolean sendMessage(String msg) {
        return sendMessage(msg.getBytes());
    }

    @Override
    public boolean sendMessage(final byte[] frame) {
        mWriteByteBuffer.writeBytes(frame);
        return true;
//        new Thread(new Runnable() {
//            @Override
//            public void run() {
//                ByteBuffer writeBuffer = ByteBuffer.wrap(frame);
//                try {
//                    mSocketChannel.write(writeBuffer);
//                    if (!writeBuffer.hasRemaining()) {
//                        //若缓冲区中无可读字节，则说明成功发送给服务器消息
//                        System.out.println("Send order 2 server succeed.");
//                    }
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
//            }
//        }).start();
//        return true;
    }

    private void doConnecting(SelectionKey key) throws IOException {
        // 由于是客户端Channel，因而可以直接强转为SocketChannel对象
        SocketChannel channel = (SocketChannel) key.channel();
        if (channel.finishConnect()) {
            setClientConnState(CONNECTED);
            getSocketListener().onOpen(clientId, null);
            //向多路复用器注册可读事件
            channel.register(mSelector, SelectionKey.OP_READ);
        } else {
            Log.i(TAG, "正在连接中。。。");
        }
    }

    private void doWrite() throws IOException {
        if (mWriteByteBuffer.readableBytes() == 0) {
            return;
        }
        ByteBuffer writeBuffer = ByteBuffer.allocate(mWriteByteBuffer.readableBytes());
        mWriteByteBuffer.readBytes(writeBuffer);
        writeBuffer.flip();
        mSocketChannel.write(writeBuffer);
        if (!writeBuffer.hasRemaining()) {
            mWriteByteBuffer.clear();
            //若缓冲区中无可读字节，则说明成功发送给服务器消息
            Log.d(TAG, "当前写缓存区数据发送成功");
        } else {
            Log.d(TAG, "当前写缓存区数据发送失败");
        }
    }

    /**
     * 读数据
     *
     * @param key
     * @return true 连接异常，false 正常读取
     * @throws IOException
     */
    private void read(SelectionKey key) throws IOException {
        SocketChannel channel = (SocketChannel) key.channel();
        ByteBuffer buffer = ByteBuffer.wrap(new byte[1024]);
        // 接收到客户端Channel的SelectionKey.OP_READ事件，说明服务器发送数据过来了，
        // 此时可以从Channel中读取数据，并且进行相应的处理
        int len = channel.read(buffer);
        if (len != -1) {
            buffer.flip();
            mReadByteBuffer.writeBytes(buffer);
            mDecoder.decode(clientId, mReadByteBuffer, mFrameList);
            //分发数据帧
            for (byte[] item : mFrameList) {
                getSocketListener().onMessage(clientId, item);
            }
        } else {
            failError(new IOException("socket is error can not read"));
        }
    }
}
